<?php

/*
 * Sistema:
 *
 * Classe TRecord
 *
 * FRAMWORK: edframe - Copyleft (cc) 2012 EDMAR Sistemas
 */


/*
 * classe TRecord
 * Esta classe provê os métodos necessários para persistir e
 * recuperar objetos da base de dados (Active Record)
 */
abstract class TRecord
{
    protected $data; // array contendo os dados do objeto

    /* método __construct()
     * instancia um Active Record. Se passado o $id, já carrega o objeto
     * @param [$id] = ID do objeto
     */
    public function __construct($id = NULL)
    {
        if ($id) // se o ID for informado
        {
            // carrega o objeto correspondente
            $object = $this->load($id);
            if ($object)
            {
                $this->fromArray($object->toArray());
            }
        }
    }

    /*
     * método __clone()
     * executado quando o objeto for clonado.
     * limpa o ID para que seja gerado um novo ID para o clone.
     */
    public function __clone()
    {
        unset($this->id);
    }

    /*
     * método __set()
     * executado sempre que uma propriedade for atribuída.
     */
    public function __set($prop, $value)
    {
        // verifica se existe método set_<propriedade>
        if (method_exists($this, 'set_'.$prop))
        {
            // executa o método set_<propriedade>
            call_user_func(array($this, 'set_'.$prop), $value);
        }
        else
        {
            if ($value === NULL)
            {
                unset($this->data[$prop]);
            }
            else
            {
                // atribui o valor da propriedade
                $this->data[$prop] = $value;
            }
        }
    }

    /*
     * método __get()
     * executado sempre que uma propriedade for requerida
     */
    public function __get($prop)
    {
        // verifica se existe método get_<propriedade>
        if (method_exists($this, 'get_'.$prop))
        {
            // executa o método get_<propriedade>
            return call_user_func(array($this, 'get_'.$prop));
        }
        else
        {
            // retorna o valor da propriedade
            if (isset($this->data[$prop]))
            {
                return $this->data[$prop];
            }
        }
    }

    /*
     * método getEntity()
     * retorna o nome da entidade (tabela)
     */
    private function getEntity()
    {
        // obtém o nome da classe
        $class = get_class($this);

        // retorna a constante de classe TABLENAME
        return constant("{$class}::TABLENAME");
    }

    /*
     * método fromArray
     * preenche os dados do objeto com um array
     */
    public function fromArray($data)
    {
        $this->data = $data;
    }

    /*
     * método toArray
     * retorna os dados do objeto como array
     */
    public function toArray()
    {
        return $this->data;
    }

    /*
     * método store()
     * armazena o objeto na base de dados e retorna
     * o número de linhas afetadas pela instrução SQL (zero ou um)
     */
    public function store($campo)
    {
        // verifica se tem ID ou se existe na base de dados
        if (empty($this->data[$campo]) or (!$this->load($campo,$this->$campo)))
        {

            // incrementa o ID
            if (empty($this->data[$campo]))
            {
                $this->$campo = $this->getLast($campo) +1;
            }

            // cria uma instrução de insert
            $sql = new TSqlInsert;
            $sql->setEntity($this->getEntity());
            // percorre os dados do objeto
            foreach ($this->data as $key => $value)
            {
                // passa os dados do objeto para o SQL
                $sql->setRowData($key, $this->$key);
            }
        }
        else
        {
            // instancia instrução de update
            $sql = new TSqlUpdate;
            $sql->setEntity($this->getEntity());
            // cria um critério de seleção baseado no ID
            $criteria = new TCriteria;
            $criteria->add(new TFilter($campo, '=', $this->$campo));
            $sql->setCriteria($criteria);
            // percorre os dados do objeto
            foreach ($this->data as $key => $value)
            {
                if ($key !== $campo) // o ID não precisa ir no UPDATE
                {
                    // passa os dados do objeto para o SQL
                    $sql->setRowData($key, $this->$key);
                }

            }
        }
        // obtém transação ativa
        if ($conn = TTransaction::get())

        {
            // faz o log e executa o SQL
            TTransaction::log($sql->getInstruction());

            $result = $conn->exec($sql->getInstruction());
            // retorna o resultado
            return $result;
        }
        else
        {
            // se não tiver transação, retorna uma exceção
            throw new Exception('Não há transação ativa!!');
        }
    }

    /*
     * método load()
     * recupera (retorna) um objeto da base de dados
     * através de seu ID e instancia ele na memória
     * @param $id = ID do objeto
     */
    public function load($campo,$id)
    {
        // instancia instrução de SELECT
        $sql = new TSqlSelect;
        $sql->setEntity($this->getEntity());
        $sql->addColumn('*');

        // cria critério de seleção baseado no ID
        $criteria = new TCriteria;
        $criteria->add(new TFilter($campo, '=', $id));

        // define o critério de seleção de dados
        $sql->setCriteria($criteria);

        // obtém transação ativa
        if ($conn = TTransaction::get())
        {
            // cria mensagem de log e executa a consulta
            TTransaction::log($sql->getInstruction());
            $result= $conn->Query($sql->getInstruction());

            // se retornou algum dado
            if ($result)
            {
                // retorna os dados em forma de objeto
                $object = $result->fetchObject(get_class($this));
            }
            return $object;
        }
        else
        {
            // se não tiver transação, retorna uma exceção
            throw new Exception('Não há transação ativa!!');
        }
    }

    /*
     * método delete()
     * exclui um objeto da base de dados através de seu ID.
     * @param $id = ID do objeto
     */
    public function delete($campo)
    {
        // o ID é o parâmetro ou a propriedade ID
        $id = $id ? $id : $this->id;

        // instancia uma instrução de DELETE
        $sql = new TSqlDelete;
        $sql->setEntity($this->getEntity());

        // cria critério de seleção de dados
        $criteria = new TCriteria;
        $criteria->add(new TFilter($campo, '=', $this->$campo));

        // define o critério de seleção baseado no ID
        $sql->setCriteria($criteria);

        // obtém transação ativa
        if ($conn = TTransaction::get())
        {
            // faz o log e executa o SQL
            TTransaction::log($sql->getInstruction());
            $result = $conn->exec($sql->getInstruction());
            // retorna o resultado
            return $result;
        }
        else
        {
            // se não tiver transação, retorna uma exceção
            throw new Exception('Não há transação ativa!!');
        }
    }

    /*
     * método getLast()
     * retorna o último ID
     */
    private function getLast($campo)
    {
        // inicia transação
        if ($conn = TTransaction::get())
        {
            // instancia instrução de SELECT
            $sql = new TSqlSelect;
            $sql->addColumn('max('.$campo.') as ID');
            $sql->setEntity($this->getEntity());

            // cria log e executa instrução SQL
            TTransaction::log($sql->getInstruction());
            $result= $conn->Query($sql->getInstruction());

            // retorna os dados do banco
            $row = $result->fetch();
            return $row[0];
        }
        else
        {
            // se não tiver transação, retorna uma exceção
            throw new Exception('Não há transação ativa!!');
        }
    }



    /*
     * método MontaCriteria()
     * monta critérios de consulta para um objeto
   */
    function MontaCriteria($obj)
    {
			$criteria = new TCriteria;
			$r = get_object_vars($this);

			foreach($r as $k => $v)
			{

			}

			$s = array_keys($v) ;

			foreach ($s as $value)
			{
				if (($value != "group") and ($value != "order") and ($value != "limit") and ($value != "offset") and ($value != "colunas"))
				{

					$op = explode("#",$this->$value);
						if (count($op) >= 2)
						{
						$repetCampo = explode("REPET", $value) ;
						if (count($repetCampo) == 2)
							$value = $repetCampo[0] ;

							if ($op[2] == 'TA')
							{ // TA transformar em arrary
								$OP = explode(",",$op[0]) ;
								$criteria->add(new TFilter($value, $op[1], $OP));
							}

else if ($op[2] == 'STR') // EXTRACT(MONTH....)
							{
								$criteria->add(new TFilter($value, 'SIMILAR TO', '%'.$op[0].'%'));
							}


							else if ($op[2] == 'EM') // EXTRACT(MONTH....)
							{
								$criteria->add(new TFilter('EXTRACT(MONTH from '.$value.')', $op[1], $op[0]));
							}
							else if ($op[2] == 'EA') // EXTRACT(YEAR....)
							{
								$criteria->add(new TFilter('EXTRACT(YEAR from '.$value.')', $op[1], $op[0]));
							}
							else
							{
								TTransaction::log('344');
								$criteria->add(new TFilter($value, $op[1], $op[0]));
							}
						}

						else
						{
							$criteria->add(new TFilter($value, '=', $this->$value));
						}
					}


				else if (($value == "group") or ($value == "order") or ($value == "limit") or ($value == "offset"))
					 $criteria->setProperty($value, $this->$value);
			}

			return $criteria ;
		}




		function nomeColunasObjeto($obj)
		{

			$colunas = '' ;
			$r = get_object_vars($this);

			foreach($r as $k => $v)
			{
			}

			$s = array_keys($v) ;

			foreach ($s as $value)
			{
				$colunas .= $value."," ;
			}

			return rtrim($colunas,",") ;
		}
}
?>
